package com.unicloud.medical.langchao;

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.unicloud.medical.oss.common.AbstractOSSService;
import com.unicloud.medical.oss.common.constant.OSSTypeConstant;
import com.unicloud.medical.oss.common.enums.ErrorEnum;
import com.unicloud.medical.oss.common.model.BucketInfo;
import com.unicloud.medical.oss.common.model.FileMetaData;
import com.unicloud.medical.oss.common.model.OSSServerProperties;
import com.unicloud.medical.oss.common.model.ObjectInfo;
import com.unisound.medical.common.exception.BusinessException;
import com.unisound.medical.common.util.AssertUtil;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Setter
@NoArgsConstructor
public class LangChaoService extends AbstractOSSService {
    private String name;
    private OSSServerProperties langChaoProperties;
    private AmazonS3 s3client;

    @Override
    public String type() {
        return OSSTypeConstant.LANG_CHAO.toLowerCase();
    }

    @Override
    public String name() {
        return name;
    }

    @Override
    public String endpoint() {
        return langChaoProperties.getEndpoint();
    }

    @Override
    public String proxyEndpoint() {
        return langChaoProperties.getProxyEndpoint();
    }

    /**
     * 查看存储桶是否存在
     */
    @Override
    public Boolean bucketExist(String bucketName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        try {
            return s3client.doesBucketExistV2(bucketName);
        } catch (Exception e) {
            log.error("bucketExist error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 创建桶
     * @param bucketName
     */
    @Override
    public Boolean createBucket(String bucketName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        try {
            Boolean bucketExists = bucketExist(bucketName);
            if (bucketExists) return true;

            s3client.createBucket(bucketName);
            return true;
        } catch (Exception e) {
            log.error("createBucket error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 设置桶策略
     *
     * @param bucketName
     * @param policyText
     */
    @Override
    public Boolean setBucketPolicy(String bucketName, String policyText) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(policyText), ErrorEnum.POLICY_TEXT_INVALID);
        try {
            s3client.setBucketPolicy(bucketName, policyText);
            return true;
        } catch (Exception e) {
            log.error("setBucketPolicy error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 删除存储桶
     */
    @Override
    public Boolean removeBucket(String bucketName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        try {
            s3client.deleteBucket(bucketName);
            return true;
        } catch (Exception e) {
            log.error("removeBucket error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取全部桶
     */
    @Override
    public List<BucketInfo> getAllBuckets() {
        try {
            List<Bucket> buckets = s3client.listBuckets();
            if (CollectionUtils.isEmpty(buckets)) return null;
            return buckets.stream()
                    .map(bucket -> new BucketInfo(bucket.getName(), bucket.getCreationDate().toString()))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("getAllBuckets error. cause={}", e.getMessage());
            return null;
        }
    }

    /**
     * 查看桶内所有的文件对象
     */
    @Override
    public List<ObjectInfo> listAllObjects(String bucketName) {
        return listObjects(bucketName, null);
    }

    /**
     * 查看桶内指定文件夹的文件对象
     */
    @Override
    public List<ObjectInfo> listObjects(String bucketName, String folder) {
        ObjectListing objectListing = getObjectListing(bucketName, folder);
        if (Objects.isNull(objectListing)) return null;

        List<S3ObjectSummary> objectSummaries = objectListing.getObjectSummaries();
        if (CollectionUtils.isEmpty(objectSummaries)) return null;

        return objectSummaries.stream()
                .map(objectSummary -> new ObjectInfo(objectSummary.getKey(), objectSummary.getSize()))
                .collect(Collectors.toList());
    }

    private ObjectListing getObjectListing(String bucketName, String folder) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        if (StringUtils.isEmpty(folder)) return s3client.listObjects(bucketName);
        return s3client.listObjects(bucketName, folder);
    }

    /**
     * 文件上传
     *
     * @param file 文件
     * @return String
     */
    @Override
    public String uploadObject(String bucketName, String objectName, FileMetaData file) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(Objects.nonNull(file) && Objects.nonNull(file.getData()) && StringUtils.isNotEmpty(file.getContentType()) && file.getSize() > 0, ErrorEnum.FILE_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(objectName), ErrorEnum.OBJECT_NAME_INVALID);
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentType(file.getContentType());
            objectMetadata.setContentLength(file.getSize());
            s3client.putObject(bucketName, objectName, new ByteArrayInputStream(file.getData()), objectMetadata);
            return accessUrl(bucketName, objectName);
        } catch (Exception e) {
            log.error("uploadObject error. cause={}", e.getMessage());
            return null;
        }
    }

    @Override
    public Boolean uploadLocalObject(String bucketName, String objectName, String fileName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(objectName), ErrorEnum.OBJECT_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(fileName), ErrorEnum.FILE_NAME_INVALID);
        try {
            File file = new File(fileName);
            if (Objects.isNull(file) || !file.exists() || file.isDirectory()) return false;

            s3client.putObject(bucketName, objectName, file);
            return true;
        } catch (Exception e) {
            log.error("uploadLocalObject error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 根据文件名称获取文件
     *
     * @param bucketName
     * @param objectName
     */
    @Override
    public InputStream getObject(String bucketName, String objectName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(objectName), ErrorEnum.OBJECT_NAME_INVALID);
        try {
            S3Object s3Object = s3client.getObject(bucketName, objectName);
            if (Objects.isNull(s3Object)) return null;
            return s3Object.getObjectContent();
        } catch (Exception e) {
            log.error("getObject error. cause={}", e.getMessage());
            return null;
        }
    }

    /**
     * 删除
     *
     * @param objectName
     */
    @Override
    public Boolean removeObject(String bucketName, String objectName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(objectName), ErrorEnum.OBJECT_NAME_INVALID);
        try {
            s3client.deleteObject(bucketName, objectName);
            return true;
        } catch (Exception e) {
            log.error("removeObject error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 批量删除
     *
     * @param objectNames
     */
    @Override
    public Boolean removeObjects(String bucketName, List<String> objectNames) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(CollectionUtils.isNotEmpty(objectNames), ErrorEnum.OBJECT_NAMES_INVALID);
        try {
            DeleteObjectsResult result = s3client.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(
                    objectNames.stream()
                            .map(objectName -> new DeleteObjectsRequest.KeyVersion(objectName))
                            .collect(Collectors.toList())));
            for (DeleteObjectsResult.DeletedObject deletedObject : result.getDeletedObjects()) {
                log.info("batchRemove objectName={} deleteMarker={}", deletedObject.getKey(), deletedObject.isDeleteMarker());
            }

            return true;
        } catch (Exception e) {
            log.error("batchRemoves  error. cause={}", e.getMessage());
            return false;
        }
    }

    /**
     * 文件迁移
     *
     * @param bucketName
     * @param sourceObjectName
     * @param targetObjectName
     */
    @Override
    public String copyObject(String bucketName, String sourceObjectName, String targetObjectName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(sourceObjectName), ErrorEnum.SOURCE_OBJECT_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(targetObjectName), ErrorEnum.TARGET_OBJECT_NAME_INVALID);
        try {
            s3client.copyObject(bucketName, sourceObjectName, bucketName, targetObjectName);
            return accessUrl(bucketName, targetObjectName);
        } catch (Exception e) {
            log.error("copyObject error. cause={}", e.getMessage());
            throw new BusinessException(ErrorEnum.COPY_OBJECT_FAIL);
        }
    }

    @Override
    public String signedUrl(String bucketName, String objectName) {
        AssertUtil.assertTrue(StringUtils.isNotEmpty(bucketName), ErrorEnum.BUCKET_NAME_INVALID);
        AssertUtil.assertTrue(StringUtils.isNotEmpty(objectName), ErrorEnum.OBJECT_NAME_INVALID);

        try {
            Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
            return s3client.generatePresignedUrl(bucketName, objectName, expiration).toString();
        } catch (Exception e) {
            log.error("signedUrl error. cause={}", e.getMessage());
            throw new BusinessException(ErrorEnum.SIGNED_URL_FAIL);
        }
    }
}

